package com.chenpp.netty.tomcat;

import com.chenpp.netty.tomcat.http.CPHttpServlet;
import com.chenpp.netty.tomcat.http.CPRequest;
import com.chenpp.netty.tomcat.http.CPResponse;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;

import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 2020/3/9
 * created by chenpp
 */
public class CPTomcat {

    private int port;

    private Properties webProperties = new Properties();

    private Map<String, CPHttpServlet> servletMapping = new HashMap<>();


    public CPTomcat(int port) {
        this.port = port;
        try {
            init();
            System.out.println("初始化tomcat 成功");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("初始化tomcat 失败");
        }
    }

    //初始化Servlet信息
    private void init() throws Exception {
        //加载web.properties
        String path = this.getClass().getResource("/").getPath();
        FileInputStream fis = new FileInputStream(path + "web.properties");
        //加载配置文件信息到Properties
        webProperties.load(fis);
        for (Object key : webProperties.keySet()) {
            if (key instanceof String && key != null && key.toString().endsWith("url")) {
                //\.url$
                String servletName = key.toString().replaceAll("\\.url$", "");
                String mappingUrl = webProperties.getProperty(key.toString());
                Class servletClass = Class.forName(webProperties.getProperty(servletName + ".className"));
                servletMapping.put(mappingUrl, (CPHttpServlet) servletClass.newInstance());
            }
        }

    }

    public void start() throws InterruptedException {
        //Netty是基于Reator模型的，需要有主线程(boss)和工作线程(worker)
        //分别定义Boss线程池和worker线程池
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            //类似于BIO中的ServerSocket 和NIO中的ServerSocketChannel
            ServerBootstrap server = new ServerBootstrap();
            server.group(bossGroup, workGroup)
                    //定义主线程处理类(Nio通道)
                    .channel(NioServerSocketChannel.class)
                    //定义worker的处理类ChildHandler
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        //对客户端的初始化处理
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //Netty对HTTP协议进行了封装，可以通过这种方法获取Netty封装的Request和Response
                            // HttpResponseEncoder 编码器
                            socketChannel.pipeline().addLast(new HttpResponseEncoder());
                            // HttpRequestDecoder 解码器
                            socketChannel.pipeline().addLast(new HttpRequestDecoder());
                            // 业务逻辑处理
                            socketChannel.pipeline().addLast(new TomcatHandler());
                        }
                    })
                    //配置Boss线程  设置最大线程数 128
                    .option(ChannelOption.SO_BACKLOG, 128)
                    //设置子线程参数， 保持长连接
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            // 绑定端口并启动服务器  ChannelFuture继承自并发包下的Future
            ChannelFuture fe = server.bind(port).sync();
            System.out.println("Tomcat 已启动，监听的端口是：" + port);
            fe.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }


    class TomcatHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            if (msg instanceof HttpRequest) {
                HttpRequest rq = (HttpRequest) msg;
                CPRequest request = new CPRequest(ctx, rq);
                CPResponse response = new CPResponse(ctx);
                //根据url获取对应的Servlet bean
                String url = request.getUrl();
                if (servletMapping.containsKey(url)) {
                    servletMapping.get(url).service(request, response);
                } else {
                    response.write("The page has gone");
                }
            }
        }
    }

}
